package com.newland.server.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 这个类用来生成token,使用的是jjwt包,这个包就是专门用来生成token的
 */
@Component
@Data
public class JwtTokenUtil {

    @Autowired
    private JwtProperties jwtProperties;//从配置文件中读取用来生成token使用到的一些固定数据,例如secret,过期时间等

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 生成token
     *
     * @param principal
     * @param currentTimeMillis
     * @return
     */
    public String generateToken(String principal, long currentTimeMillis) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("sub", principal);
        claims.put("created", String.valueOf(currentTimeMillis));
        long l = jwtProperties.getExpiration() + currentTimeMillis;
        claims.put("exp_date",l);//把不敏感的主要信息放进claims
        return generateToken(claims);
    }

    private String generateToken(Map<String, Object> claims) {
        Date expirationDate = new Date(System.currentTimeMillis() + jwtProperties.getExpiration());
        return Jwts.builder()
                .setClaims(claims)
//                .setExpiration(expirationDate)//应该在在这里以这种方式设置过期时间的，但是这样一旦token过期，获取claims
                .signWith(SignatureAlgorithm.HS512, jwtProperties.getSecret())
                .compact();//固定套路生成token
    }

    /**
     * 从token中获得主身份信息
     *
     * @param token
     * @return
     */
    public String getPrincipalFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        String principal = claims.getSubject();
        return principal;
    }

    private Claims getClaimsFromToken(String token) {
        Claims claims = null;
//        try {
//            claims = Jwts.parser().setSigningKey(jwtProperties.getSecret()).parseClaimsJws(token).getBody();
//        } catch (ExpiredJwtException e) {
//            e.printStackTrace();
//        } catch (UnsupportedJwtException e) {
//            e.printStackTrace();
//        } catch (MalformedJwtException e) {
//            e.printStackTrace();
//        } catch (SignatureException e) {
//            e.printStackTrace();
//        } catch (IllegalArgumentException e) {
//            e.printStackTrace();
//        }
        claims = Jwts.parser().setSigningKey(jwtProperties.getSecret()).parseClaimsJws(token).getBody();
        return claims;
    }

    /**
     * 判断token是否过期
     *
     * @param token
     * @return
     */
    public boolean isTokenExpired(String token) {
        Claims claims = getClaimsFromToken(token);//从token中拿到关于账户的相关信息,就是一个map
//            Date expirationDate = claims.getExpiration();
        long expiration = (long) claims.get("exp_date");//找到这个过期时间
        if(System.currentTimeMillis()<=expiration){//当前时间和过期时间比较,查看token是否过期
            return false;
        }
        return true;
    }

    /**
     * 刷新token
     *
     * @param token
     * @return
     */
    public String refreshToken(String token, long currentTimeMillis) {
        String refreshToken = null;
        try {
            refreshToken = "";
            Claims claims = getClaimsFromToken(token);
            claims.put("created", String.valueOf(currentTimeMillis));//刷新token主体其他信息不变,只需要改变过期时间即可
            long l = jwtProperties.getExpiration() + currentTimeMillis;
            claims.put("exp_date",l);
            refreshToken = generateToken(claims);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return refreshToken;
    }


    /**
     * 验证token
     *
     * @param token
     * @return
     */
    public boolean validateToken(String token) {
        String principalFromToken = getPrincipalFromToken(token);//从token中获得用户名
        String tokenRedis = stringRedisTemplate.opsForValue().get(principalFromToken);//利用这个用户名从redis中找对应的值
        Claims claims = getClaimsFromToken(token);//利用token拿到当前用户的相关信息
        String created = (String) claims.get("created");//拿到过期时间
        return tokenRedis != null && created.equals(tokenRedis);//当redis中存在当前用户对应的时间戳并且token中的时间戳和redis保存的时间戳一样就是验证成功了
    }
}
